package com.hqd.jjvm.rtda.memory;

import jdk.nashorn.internal.ir.debug.ObjectSizeCalculator;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;

import java.lang.reflect.Field;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;

/**
 * 1)创建堆大小（用数组模拟）
 * 2）初始化空闲列表（仅仅存放GC_ROOT）
 * 3）对象放进堆中（数组），超过80%进行gc，不足抛出OOM
 * gc:标记存活对象，设置状态为true
 * 清理:设置状态为false，且将释放下标放回空闲列表
 * 根节点:当前栈帧的引用对象（OopMap）
 */
public class HeapArea {
	protected static final int MAX_HEAP_SIZE = 10;
	private static final long OBJ_HEAD_SIZE = ObjectSizeCalculator.getEffectiveMemoryLayoutSpecification().getObjectHeaderSize();
	private static final HeapObj[] objs = new HeapObj[MAX_HEAP_SIZE];
	static LinkedList<Integer> freeList = new LinkedList<>();

	static {
		for (int i = 0; i < objs.length; i++) {
			freeList.add(i);
		}
	}

	public static void main(String[] args) throws IllegalAccessException, InstantiationException {
		HeapObj obj = new HeapObj(new AA(2202, new BB(11)));
		GCRoot gcRoot = new GCRoot();
		gcRoot.addRoot(obj);
		allot(gcRoot, obj);
		HeapObj obj1 = new HeapObj(new CC());
		allot(gcRoot, obj1);
		HeapObj obj2 = new HeapObj(new AA(11, new BB(1111)));
		allot(gcRoot, obj2);
		HeapObj obj3 = new HeapObj(new AA(11, new BB(1111)));
		allot(gcRoot, obj3);
		HeapObj obj4 = new HeapObj(new AA(11, new BB(1111)));
		allot(gcRoot, obj4);
		HeapObj obj5 = new HeapObj(new AA(11, new BB(1111)));
		allot(gcRoot, obj5);
	}

	public static void sweep(GCRoot root) {
		mark(root);
		for (int i = 0; i < objs.length; i++) {
			if (objs[i] == null) {
				if (!freeList.contains(i)) {
					freeList.addLast(i);
				}
			} else {
				if (!objs[i].isALive()) {
					objs[i].setALive(false);
					freeList.addLast(i);
				}
			}
		}
	}

	public static void mark(GCRoot root) {
		for (int i = 0; i < root.getRoots().size(); i++) {
			mark(root.getRoots().get(i));
		}
	}

	public static void mark(HeapObj heapObj) {
		if (heapObj != null && heapObj.isALive() == false) {
			heapObj.setALive(true);
			List<HeapObj> children = children(heapObj);
			for (int i = 0; i < children.size(); i++) {
				mark(children.get(i));
			}
		}
	}

	/**
	 * 获取引用类型
	 *
	 * @param heapObj
	 */
	public static List<HeapObj> children(HeapObj heapObj) {
		List<HeapObj> list = new LinkedList<>();
		Object obj = heapObj.getObject();
		Field[] fields = obj.getClass().getDeclaredFields();
		try {
			for (Field field : fields) {
				field.setAccessible(true);
				if (!field.getType().isPrimitive()) {
					list.add(new HeapObj(field.get(obj)));
				}
			}
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		}
		return list;
	}

	public static void allot(GCRoot root, HeapObj heapObj) throws IllegalAccessException {
		if (freeList.size() < 2) {
			System.out.println(freeList.size());
			sweep(root);
			System.out.println(freeList.size());
		}
		if (freeList.size() == 0) {
			throw new OutOfMemoryError("内存溢出");
		}
		int position = freeList.pollFirst();
		objs[position] = heapObj;
		heapObj.setPosition(position);
		List<HeapObj> children = children(heapObj);
		for (int i = 0; i < children.size(); i++) {
			allot(root, children.get(i));
		}

	}

	public static void test() {
		ByteBuffer buff = ByteBuffer.allocate(Integer.BYTES);
		buff.putInt(2202);
		byte[] arr = buff.array();
		System.out.println(ByteBuffer.wrap(arr).getInt());
		System.out.println(Arrays.toString(buff.array()));
	}

	public static int objectDecompose(Object o) throws IllegalAccessException {
		int size = 0;
		Field[] fields = o.getClass().getDeclaredFields();
		for (Field field : fields) {
			if (field.getType().isPrimitive()) {
				size += getPrimitiveTypeSize(field.getType());
			} else {
				field.setAccessible(true);
				return size + objectDecompose(field.get(o));
			}
		}
		return size;
	}

	public static int getPrimitiveTypeSize(Class clazz) {
		if (clazz == boolean.class) {
			return 4;
		} else if (clazz == byte.class) {
			return 1;
		} else if (clazz == short.class) {
			return 2;
		} else if (clazz == char.class) {
			return 4;
		} else if (clazz == int.class) {
			return 4;
		} else if (clazz == float.class) {
			return 4;
		} else if (clazz == double.class) {
			return 8;
		} else if (clazz == long.class) {
			return 8;
		}
		throw new Error("");
	}
}

@AllArgsConstructor
@Setter
@Getter
class GCRoot {
	private List<HeapObj> roots;

	public GCRoot() {
		roots = new LinkedList<>();
	}

	public void addRoot(HeapObj heapObj) {
		this.roots.add(heapObj);
	}
}

@Getter
@Setter
class HeapObj {
	private int position;
	private long size;
	private boolean isALive;
	private Object object;


	public HeapObj(Object object) {
		this.object = object;
		this.size = ObjectSizeCalculator.getObjectSize(object);
		this.isALive = false;
	}
}

@AllArgsConstructor
class AA {
	private int a;
	private BB b;
}

@AllArgsConstructor
class BB {
	private int b;
}

class CC {
	private int c;
}